package ar.edu.itba.it.pdc.httpParser;

import ar.edu.itba.it.pdc.httpParser.exceptions.HttpBadRequestException;
import ar.edu.itba.it.pdc.httpParser.interfaces.Parser;
import ar.edu.itba.it.pdc.httpParser.models.HttpBody;
import ar.edu.itba.it.pdc.httpParser.models.Token;
import ar.edu.itba.it.pdc.httpParser.models.TokenType;

public class ChunkedBodyParser implements Parser {

	private enum State {
		START, CHUNKLENGTH, CHUNKDATA, CHUNKEND, BODYEND
	};

	private State state = State.START;
	
	private Integer chunkLength = null;
	
	private HttpBody model = new HttpBody();
	
	private Integer bodyLength = 0;

	public ChunkedBodyParser() {

	}

	@Override
	public int parseToken(Token token) throws HttpBadRequestException {

		switch (state) {

		case START:

			if (token.isType(TokenType.WORD) && isHexaDigit(token)) {
				state = State.CHUNKLENGTH;
				parseToken(token);
			} else {
				throw new HttpBadRequestException(token);
			}
			break;
		case CHUNKLENGTH:

			if (chunkLength == null) {

				chunkLength = Integer.parseInt(token.toString(), 16);
				if (chunkLength == 0) {
					state = State.CHUNKEND;
				}
				bodyLength += chunkLength;
			} else if (token.isType(TokenType.SPACE)) { // not sure if we must
														// handle this

			} else if (token.isType(TokenType.CRLF)) {
				state = State.CHUNKDATA;
			} else {
				throw new HttpBadRequestException(token);
			}

			break;

		case CHUNKDATA:

			chunkLength -= token.length();
			model.append(token);
			if (chunkLength < 0) {

				throw new HttpBadRequestException(token);
			} else if (chunkLength == 0) {
				chunkLength = null;
				state = State.CHUNKEND;
			}
			break;

		case CHUNKEND:
			
			if (token.isType(TokenType.CRLF)) {
				state = State.START;
			} else {
				throw new HttpBadRequestException(token);
			}
			
			if (chunkLength != null && chunkLength == 0){
				state = State.BODYEND;
			}
			break;
			
		case BODYEND:
			
			if (token.isType(TokenType.CRLF)) {
				model.append(token);
				bodyLength++;
				return 0;
			} else {
				throw new RuntimeException("The trailer of a chunked body is not supported.");
			}	
		}


		return -1;
	}

	private boolean isHexaDigit(Token token) {

		String str = token.toString().toUpperCase();
		for (char c : str.toCharArray()) {
			if (!Character.isDigit(c) && c != 'A' && c != 'B' && c != 'C'
					&& c != 'D' && c != 'E' && c != 'F') {
				return false;
			}
		}
		return true;
	}

	@Override
	public HttpBody getModel() {
		return model;
	}

	public Integer getBodyLength() {
		return bodyLength;
	}
	
}
